package com.sojson.util.encrypt.bothway.imparity.sm2.impl.encrypt;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.sojson.util.StringUtil;
import com.sojson.util.encrypt.bothway.imparity.bean.CustomKeyPair;
import com.sojson.util.encrypt.bothway.imparity.sm2.IKeyPairGenerator;
import com.sojson.util.encrypt.bothway.imparity.sm2.ISm2Util;
import com.sojson.util.encrypt.bothway.imparity.sm2.Sm2Util;
import com.sojson.util.encrypt.bothway.imparity.sm2.bean.Sm2KeyPair;

import lombok.extern.slf4j.Slf4j;

/**
 * Sms2双向非对称加解密工具类
 * 
 * @author liu
 * @date 2020-09-04
 */
@Slf4j
public class Sm2UtilImpl implements ISm2Util, IKeyPairGenerator {

    private static Sm2UtilImpl instances;

    private final SM3Digest DIGEST = new SM3Digest();
    private final String SM2P256V1 = "sm2p256v1";
    /** 使用标准名称创建EC参数生成的参数规范 */
    private final ECGenParameterSpec SPEC = new ECGenParameterSpec(SM2P256V1);
    /** 获取一个椭圆曲线类型的密钥对生成器 */
    private KeyPairGenerator KPG;

    {
        try {
            KPG = KeyPairGenerator.getInstance(EC, new BouncyCastleProvider());

            // 使用SM2算法域参数集初始化密钥生成器（默认使用以最高优先级安装的提供者的 SecureRandom 的实现作为随机源）
            /// kpg.initialize(sm2Spec);

            // 使用SM2的算法域参数集和指定的随机源初始化密钥生成器
            KPG.initialize(SPEC, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
        } catch (InvalidAlgorithmParameterException e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 加密
     * 
     * @param data      内容
     * @param publicKey 公钥
     * @return
     * @throws Exception 
     */
    @Override
    public String encrypt(String data, String publicKey) throws Exception {
        return encryptToStr(data.getBytes(), publicKey);
    }

    /**
     * 加密
     * 
     * @param data      内容
     * @param publicKey 公钥
     * @return
     * @throws Exception 
     */
    @Override
    public byte[] encrypt(byte[] data, String publicKey) throws Exception {
        return encryptToStr(data, publicKey).getBytes();
    }

    /**
     * 加密
     * 
     * @param data      内容
     * @param publicKey 公钥
     * @return
     * @throws Exception 
     */
    @Override
    public byte[] encryptToByte(String data, String publicKey) throws Exception {
        return encrypt(data.getBytes(), publicKey);
    }

    /**
     * 加密
     * 
     * @param data      内容
     * @param publicKey 公钥
     * @return
     * @throws Exception 
     */
    @Override
    public String encryptToStr(byte[] data, String publicKey) throws Exception {
        return Sm2Util.encryptContentToStr(encryptInit(data, publicKey));
    }

    /**
     * 加密
     * 
     * @param data      内容
     * @param publicKey 公钥
     * @return
     * @throws Exception 
     */
    private byte[] encryptInit(byte[] data, String publicKey) throws Exception {
        ECPublicKeyParameters publicKeyToParams = publicKeyToParams(ALGORITHM, publicKey);

        CipherParameters pubKeyParameters = new ParametersWithRandom(publicKeyToParams);
        SM2Engine engine = new SM2Engine(DIGEST);
        engine.init(true, pubKeyParameters);
        return engine.processBlock(data, 0, data.length);
    }

    /**
     * 解密
     * 
     * @param data          内容
     * @param privateKey    私钥
     * @return
     * @throws Exception 
     */
    @Override
    public String decrypt(String data, String privateKey) throws Exception {
        return new String(decryptToByte(data, privateKey));
    }

    /**
     * 解密
     * 
     * @param data          内容
     * @param privateKey    私钥
     * @return
     * @throws Exception 
     */
    @Override
    public byte[] decrypt(byte[] data, String privateKey) throws Exception {
        return decryptToByte(new String(data), privateKey);
    }

    /**
     * 解密
     * 
     * @param data          内容
     * @param privateKey    私钥
     * @return
     * @throws Exception 
     */
    @Override
    public byte[] decryptToByte(String data, String privateKey) throws Exception {
        return decryptInit(Sm2Util.decryptContentToByte(data), privateKey);
    }

    /**
     * 解密
     * 
     * @param data          内容
     * @param privateKey    私钥
     * @return
     * @throws Exception 
     */
    @Override
    public String decryptToStr(byte[] data, String privateKey) throws Exception {
        return decrypt(new String(data), privateKey);
    }

    /**
     * 解密
     * 
     * @param data          内容
     * @param privateKey    私钥
     * @return
     * @throws Exception 
     */
    private byte[] decryptInit(byte[] data, String privateKey) throws Exception {
        CipherParameters privateKeyParameters = privateKeyToParams(ALGORITHM, privateKey);
        SM2Engine engine = new SM2Engine(DIGEST);
        engine.init(false, privateKeyParameters);
        return engine.processBlock(data, 0, data.length);
    }

    /**
     * 获得公私钥对
     * @return 
     * @throws Exception 
     */
    @Override
    public CustomKeyPair generateKeyPair() throws Exception {
        // 通过密钥生成器生成密钥对
        KeyPair generateKeyPair = KPG.generateKeyPair();
        return new Sm2KeyPair(generateKeyPair.getPublic(), generateKeyPair.getPrivate());
    }

    /**
     * 判断一个对象是否是空(空对象)
     * 
     * @param obj
     * @return
     */
    private static boolean isBlankObject(Object obj) {
        return StringUtil.isBlankObject(obj);
    }

    public static Sm2UtilImpl getInstances() {
        if (isBlankObject(instances)) {
            instances = new Sm2UtilImpl();
        }
        return instances;
    }

}